DEBUG = True

import time
import urllib
from threading import Thread
import tarfile
import sys
import os
import shutil
from glob import glob

bRun = True

try:
    from Versions import LatestDataVersions
except:
    print "Error: There is a problem with the LatestDataVersions file."
    print "Terminating..."
    bRun = False
    


    
# If they don't have a CurrentDLLVersion python file yet.
CURRENT_DLL = 0
CURRENT_MEDIA = 0
CURRENT_OTHER = 0

if bRun:
    try:
        from Versions import CurrentDLLVersion
        CURRENT_DLL = CurrentDLLVersion.iCurrentVersion
    except ImportError:
        if DEBUG:
            print "No DLL version file found. Assuming 0"

    # If they don't have a CurrentMediaVersion python file yet.
    try:
        from Versions import CurrentMediaVersion
        CURRENT_MEDIA = CurrentMediaVersion.iCurrentVersion
    except ImportError:
        if DEBUG:
            print "No media version file found. Assuming 0"

    try:
        from Versions import CurrentOtherVersion
        CURRENT_OTHER = CurrentOtherVersion.iCurrentVersion
    except ImportError:
        if DEBUG:
            print "No Other version file found. Assuming 0"

print

PROGRESS_BAR_SIZE = 50
VERSION_DIR = "Versions"
TEMP_DIR = "Versions/Temp"

def getDLLVersions():
    """
    Gets the version info from the LatestDataVersion file
    """
    return LatestDataVersions.getDLLVersions()

def getMediaVersions():
    """
    Gets the version info from the LatestDataVersion file
    """
    return LatestDataVersions.getMediaVersions()

def getOtherVersions():
    """
    Gets the version info from the LatestDataVersion file
    """
    return LatestDataVersions.getOtherVersions()

def getDLLCurrentVersion():
    """
    Gets the DLL version currently installed on the computer, as an integer
    """
    return CURRENT_DLL

def getMediaCurrentVersion():
    """
    Gets the Media version currently installed on the computer, as an integer
    """
    return CURRENT_MEDIA

def getOtherCurrentVersion():
    """
    Gets the Other version currently installed on the computer, as an integer
    """
    return CURRENT_OTHER

def msg(txt):
    """
    Helper functions. Writes msg to stdout without adding a newline. This allows for the same text to be overridden.
    """
    sys.stdout.write(txt)
    sys.stdout.flush()
    

class ExtractThread( Thread ):
    """
    Thread used to extract the file, so a progress bar can write out it's progress.
    """
    def __init__(self, szTarballFilename):
        self.iProgressSize = -1
        self.iTotalSize = -1
        self.szCurrentFile = ""
        self.szTarballFilename = szTarballFilename
        Thread.__init__(self)

    def run(self):
        self.iProgressSize = 0

        pFile = tarfile.open(self.szTarballFilename, "r:gz")
        pMembers = pFile.getmembers()

        # Determine the size of the package
        iTotalSize = 0
        for pMember in pMembers:
            iTotalSize += pMember.size

        self.iTotalSize = iTotalSize

        # Go through extracting each member
        for pMember in pMembers:
            self.szCurrentFile = pMember.name
            pFile.extract( pMember.name, TEMP_DIR )
            self.iProgressSize += pMember.size

        pFile.close()

    def getProgressSize(self):
        return self.iProgressSize

    def getTotalSize(self):
        return self.iTotalSize

    def getCurrentFile(self):
        return self.szCurrentFile


def printProgressBar( iProgress, iMax ):
    sz = "<"

    fProgress = float(iProgress)
    fMax = float(iMax)
    iDots = 0

    iRealProgress = int ( (fProgress / fMax) * float(PROGRESS_BAR_SIZE) )

    if iRealProgress > PROGRESS_BAR_SIZE:
        iRealProgress = PROGRESS_BAR_SIZE
    
    for i in range (0, iRealProgress):
        iDots += 1
        sz += "*"
    for i in range(iRealProgress, PROGRESS_BAR_SIZE):
        iDots += 1
        sz += "-"
    sz += ">\t"
    fPercent = ((fProgress / fMax) * 100)
    if fPercent > 100:
        fPercent = 100.0

    if fPercent < 0:
        fPercent = 0.0
    sz += "(%d %%)" % ( fPercent , )

    sz += chr(13)

    
    msg(sz)

def uncompress(szTarball, iVersionNumberStart, iVersionNumberEnd):
    """
    Function to start a new thread to extract a tarball and make
    a progress bar of it's progress.
    """
    print "Extracting %s (Update version %d to %d)...\n\n" % (szTarball, iVersionNumberStart, iVersionNumberEnd)
    pThread = ExtractThread(szTarball)
    pThread.start()
    while pThread.isAlive():
        time.sleep(0.05)
        if pThread.getTotalSize() != -1:
            printProgressBar( pThread.getProgressSize(), pThread.getTotalSize(), )

    print "\n"

def getFormattedData(pVersionData):
    """
    Formats the version data into something that can be easily readable
    """
    szString = "\tRelease Date: %s" % (pVersionData["Date"], )
    szString += "\n"
    szString += "\tDownload Size: %s" % ( "%.2f %s" % (pVersionData["Compressed Size"], "MB"), )
    szString += "\n"
    szString += "\tFile: %s" % ( os.path.join(VERSION_DIR, pVersionData["Filename"]), )
    szString += "\n"
    return szString

def cmpVersions(x, y):
    return x["Version"] - y["Version"]

def getVersionsNeedingDownloads(pVersionList, iCurrentVersion):

    pVersionList.sort(cmp=cmpVersions, reverse = True)
    
    pVersionsToDownload = []
    iBackwardsTo = -1

    for pVersion in pVersionList:
        if pVersion["Version"] > iCurrentVersion:
            if pVersion["Version"] < iBackwardsTo or iBackwardsTo == -1:
                pVersionsToDownload.append(pVersion)
                if pVersion["Overrides"] != -1:
                    iBackwardsTo = pVersion["Overrides"]


    return pVersionsToDownload

def showUpdateInfo(pDataToDownload, szTypeString):
    bGetData = False

    if pDataToDownload == None:
        print "Error: Could not retrieve latest data"
        # TODO
    else:
        if len(pDataToDownload) == 0:
            print "Your media files are up to date."
            bGetData = False
        elif len(pDataToDownload) > 0:
            print "Newer %s files are available:\n\n" % szTypeString
            for pData in pDataToDownload:
                print getFormattedData(pData)

            strInput = raw_input( "Do you wish to download these updates? [Y]es, [N]o  : ")
            if strInput.upper() == "Y" or strInput.upper() == "YES":
                bGetData = True

    return bGetData

def getInputs():
    """
    Returns a 2-tuple(bool, bool) of if the user should get the DLL
    and/or the Media downloads.
    """
    bGetDLL = False
    bGetMedia = False
    bGetOther = False

    print "Checking for DLL updates..."

    pDataToDownload = getVersionsNeedingDownloads (getDLLVersions(), getDLLCurrentVersion() )
    bGetDLL = showUpdateInfo( pDataToDownload, "DLL" )

    print "\n"

    print "Checking for Media updates..."

    pDataToDownload = getVersionsNeedingDownloads( getMediaVersions(), getMediaCurrentVersion() )
    bGetMedia = showUpdateInfo( pDataToDownload, "Media" )

    print "\n"

    print "Checking for Other updates..."

    pDataToDownload = getVersionsNeedingDownloads( getOtherVersions(), getOtherCurrentVersion() )
    bGetOther = showUpdateInfo( pDataToDownload, "Other" )

    print "\n"
    return (bGetDLL, bGetMedia, bGetOther)

def downloadProgress(iCountBlocks, iBlockSizeBytes, iTotalSize):
    printProgressBar(iCountBlocks * iBlockSizeBytes, iTotalSize)

def downloadUpdate(szURL, szFilename, iVersionNumberStart, iVersionNumberEnd):
    print "Downloading %s\n(Update version %d to %d)...\n\n" % (szURL, iVersionNumberStart, iVersionNumberEnd)
    urllib.urlretrieve(szURL, szFilename, reporthook=downloadProgress)
    print "\n\n"

def installChanges():
    """
    Copies all the files in the temp directory to the root Planetfall folder.
    """
    print "Installing patch into root directory..."
    
    for dirpath, dirnames, filenames in os.walk(TEMP_DIR):
        szPath = dirpath.replace(TEMP_DIR, "")

        if szPath == "":
            # On the head directory, just make it the curdir
            szPath = os.path.curdir
        else:
            # On other directories, we need to remove the first slash
            szPath = szPath[1:]
            
        if not os.path.exists(szPath):
            os.mkdir(szPath)

        for filename in filenames:
            print "Creating %s" % os.path.join(szPath, filename)
            shutil.copy(os.path.join(dirpath, filename), os.path.join(szPath, filename))

        print

    
def downloadAndInstallUpdate(pVersionUpdate):
    iVersionStart = pVersionUpdate["Overrides"]
    iVersionEnd = pVersionUpdate["Version"]

    # Create temp dir
    if not os.path.exists(TEMP_DIR):
        os.mkdir(TEMP_DIR)

    if iVersionStart == -1:
        iVersionStart = iVersionEnd - 1

    szFilename = os.path.join(VERSION_DIR, pVersionUpdate["Filename"].split("/")[-1])

    print "------------------------------------------------------"
    print "-                                                    -"
    print "- Installing patch %4d - %4d                       -" % (iVersionStart, iVersionEnd)
    print "-                                                    -"
    print "------------------------------------------------------"

    downloadUpdate(pVersionUpdate["Filename"], szFilename, iVersionStart, iVersionEnd)
    uncompress(szFilename, iVersionStart, iVersionEnd)
    installChanges()
    os.remove(szFilename)    

    # Delete the temp directory
    shutil.rmtree(TEMP_DIR)
        
    

if __name__ == '__main__':

    if bRun:
    
        try:
            bGetDLL, bGetMedia, bGetOther = getInputs()

            if bGetMedia or bGetDLL or bGetOther:

                if bGetDLL:
                    pDLLData = getVersionsNeedingDownloads( getDLLVersions(), getDLLCurrentVersion() )
                    pDLLData.sort(cmpVersions)
                    for pData in pDLLData:
                        downloadAndInstallUpdate(pData)

                if bGetMedia:
                    pMediaData = getVersionsNeedingDownloads( getMediaVersions(), getMediaCurrentVersion() )
                    pMediaData.sort(cmpVersions)
                    for pData in pMediaData:
                        downloadAndInstallUpdate(pData)

                if bGetOther:
                    pOtherData = getVersionsNeedingDownloads( getOtherVersions(), getOtherCurrentVersion() )
                    pOtherData.sort(cmpVersions)
                    for pData in pOtherData:
                        downloadAndInstallUpdate(pData)

            # Clear out the .pyc files in the Versions directory
            for szFilename in glob(os.path.join("Versions", "*.pyc")):
                os.remove(szFilename)

            print "\n\nUpdate script has finished."
            print "Press any key to continue"
        except e:
            print "An error was encountered...please post a copy or screenshot of this."
            print "%s" % e

    t = raw_input("Enter any key to continue...")
